package com.azzcs.spring.factory;

import com.azzcs.spring.bean.BeanDefinition;
import com.azzcs.spring.bean.PropertyValue;
import com.azzcs.spring.factory.config.BeanFactoryAware;
import com.azzcs.spring.factory.config.BeanFactoryPostProcessor;
import com.azzcs.spring.factory.config.BeanPostProcessor;
import com.azzcs.spring.factory.config.ConfigurationClassPostProcessor;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: wzg
 * @Date: 2020/12/19 下午3:27
 */
public class DefaultListableBeanFactory implements BeanFactory {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    private final Set<String> registeredSingletons = new HashSet<>();

    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>();

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    private final Set<String> beanDefinitionNames = new HashSet<>();

    private final Set<BeanFactoryPostProcessor> beanFactoryPostProcessors = new HashSet<>();

    private final Set<BeanPostProcessor> beanPostProcessors = new TreeSet<>();

    private final Set<BeanFactoryAware> beanFactoryAwares = new HashSet<>();

    private final Map<String, String> alias = new ConcurrentHashMap<>();

    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) {
        String alias = this.alias.get(requiredType.getName());

        if(alias == null || alias.equals("")){
            throw new RuntimeException(requiredType.getName()+" bean not exist");
        }
        return (T)getBean(alias);
    }

    @Override
    public boolean containsBean(String name) {
        return registeredSingletons.contains(name);
    }

    private Object doGetBean(String name) {
        Object bean = getSingleton(name);
        if (bean == null) {
            bean = getSingleton(name,()->createBean(name));
        }
        return bean;
    }

    private Object createBean(String name) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if(beanDefinition == null){
            throw new RuntimeException(name+" bean is not exist");
        }
        return doCreateBean(beanDefinition);
    }

    private Object doCreateBean(BeanDefinition beanDefinition) {
        try {
            Object bean = beanDefinition.getBeanClass().newInstance();
            // 暴露到三级缓存 三级缓存存储 bean 创建的 工厂方法
            singletonFactories.put(beanDefinition.getBeanName(),()->getEarlyBeanReference(beanDefinition,bean));
            Object exposedObject = bean;
            // 填充属性
            populateBean(beanDefinition);
            // 实例化
            exposedObject = initializeBean(exposedObject,beanDefinition);
            return exposedObject;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("bean create is error");
        }
    }

    private Object initializeBean(Object bean, BeanDefinition beanDefinition) {
        bean = applyBeanPostProcessorsBeforeInitialization(bean, beanDefinition.getBeanName());
        invokeAwareMethods(bean,beanDefinition.getBeanName());
        invokeInitMethods(bean,beanDefinition.getBeanName());
        bean = applyBeanPostProcessorsAfterInitialization(bean, beanDefinition.getBeanName());
        return bean;
    }

    // 调用 BeanFactoryAware 接口
    private void invokeAwareMethods(Object bean, String beanName) {
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
    }

    // 后置处理器
    private Object applyBeanPostProcessorsAfterInitialization(Object bean, String beanName) {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

    // init 方法
    private void invokeInitMethods(Object o, Object bean) {
    }

    // 前置处理器
    private Object applyBeanPostProcessorsBeforeInitialization(Object bean, String beanName) {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
        }
        return bean;
    }

    private void populateBean(BeanDefinition beanDefinition) {
        Set<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            Object bean = null;
            try {
                bean = getBean(propertyValue.getName());
            }catch (Exception e){
                System.out.println(propertyValue.getName()+"by name autowired failure");
            }
            if(bean == null) {
                bean = getBean(propertyValue.getClazz());
            }
            propertyValue.setValue(bean);
        }
    }

    private Object getEarlyBeanReference(BeanDefinition beanDefinition, Object bean) {
        return bean;
    }

    // 从三级缓存取 bean
    private Object getSingleton(String name) {
        Object bean = singletonObjects.get(name);
        if(bean == null){
            bean = earlySingletonObjects.get(name);
            if(bean == null){
                ObjectFactory<?> objectFactory = singletonFactories.get(name);
                if(objectFactory != null){
                    bean = objectFactory.getObject();
                    singletonObjects.remove(name);
                    earlySingletonObjects.put(name,bean);
                }
            }
        }
        return bean;
    }

    private Object getSingleton(String name,ObjectFactory factory) {
        synchronized (this.singletonObjects){
            Object bean = this.singletonObjects.get(name);
            if(bean == null){
                bean = factory.getObject();
                // 注册 bean
                addSingleton(name, bean);
            }
            return bean;
        }
    }

    private void addSingleton(String name, Object bean) {
        singletonObjects.put(name,bean);
        earlySingletonObjects.remove(name);
        singletonFactories.remove(name);
        registeredSingletons.add(name);
    }

    public void doRegisterBean(BeanDefinition beanDefinition) {
        if (beanDefinitionNames.contains(beanDefinition.getBeanName())) {
            return;
        }
        beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
        beanDefinitionNames.add(beanDefinition.getBeanName());
        Set<String> alias = beanDefinition.getAlias();
        for (String alia : alias) {
            this.alias.put(alia,beanDefinition.getBeanName());
        }
    }

    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
        beanFactoryPostProcessors.add(beanFactoryPostProcessor);
    }

    public void invokeBeanFactoryPostProcessors() {
        // 注册 BeanFactoryPostProcessor
        List<BeanFactoryPostProcessor> registryProcessors = new ArrayList<>();
        // 普通 BeanFactoryPostProcessor
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessors) {
            if (beanFactoryPostProcessor instanceof ConfigurationClassPostProcessor) {
                registryProcessors.add(beanFactoryPostProcessor);
            } else {
                regularPostProcessors.add(beanFactoryPostProcessor);
            }
        }
        doInvokeBeanFactoryPostProcessors(registryProcessors);
        doInvokeBeanFactoryPostProcessors(regularPostProcessors);
    }

    // 执行 BeanFactoryPostProcessors
    private void doInvokeBeanFactoryPostProcessors(List<BeanFactoryPostProcessor> regularPostProcessors) {
        for (BeanFactoryPostProcessor postProcessor : regularPostProcessors) {
            postProcessor.postProcessBeanFactory(this);
        }
    }


    /**
     * 注册自定义 BeanPostProcessors
     *
     */
    public void registerBeanPostProcessors() {

    }

    public void finishBeanFactoryInitialization() {
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
        for (String beanName : beanNames) {
            getBean(beanName);
        }
    }

    public Map<String, BeanDefinition> getBeanDefinitionMap() {
        return beanDefinitionMap;
    }

    public void addBeanFactoryAware(Object bean) {
        beanFactoryAwares.add((BeanFactoryAware)bean);
    }

    public void addBeanBeanPostProcessor(Object bean) {
        beanPostProcessors.add((BeanPostProcessor) bean);
    }
}
